package com.swfgui.loader
{
	import com.swfgui.debug.Logger;
	
	import flash.display.Bitmap;
	import flash.display.DisplayObject;
	import flash.display.Loader;
	import flash.display.LoaderInfo;
	import flash.display.MovieClip;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.events.UncaughtErrorEvent;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.system.ApplicationDomain;
	import flash.system.LoaderContext;
	import flash.system.Security;
	import flash.system.SecurityDomain;
	import flash.utils.ByteArray;
	import flash.utils.Dictionary;
	import flash.utils.setTimeout;

	[Event(name="progress", type="com.swfgui.loader.SLoaderEvent")]

	[Event(name="complete", type="com.swfgui.loader.SLoaderEvent")]

	[Event(name="error", type="com.swfgui.loader.SLoaderEvent")]

	/**
	 * 方便的资源加载类
	 * @author llj
	 */
	public class LoaderItem extends EventDispatcher
	{		
		public var timeOutCount:int;
		/**
		 * 供用户临时存储数据用的
		 * @default
		 */
		public var userData:*;

		/**
		 * 资源的版本列表，放置浏览器缓存
		 * @default
		 */
		public var revisionList:Dictionary;

		public var baseUrl:String = null;

		private var _url:String;

		private var _loader:Loader;
		private var _urlLoader:URLLoader;
		private var _dataFormat:String;

		private var _useCurrentDomain:Boolean = false;
		private var _checkPolicyFile:Boolean;

		private var _loaderInfo:LoaderInfo;

		private var params:Array;
		private var onComplete:Function;
		private var onError:Function;
		private var onProgress:Function;

		private var _errorText:String;

		/**
		 * 最好不要重复利用此类的实例，因为重复调用loadOne或loadMany可能会打断正在
		 * 加载的过程。
		 */
		public function LoaderItem(url:String)
		{
			_url = url;
			_dataFormat = getFileType(url);
			if (_dataFormat == SLoaderDataFormat.DISPLAY)
			{
				_loader = new Loader();
				_loaderInfo = loader.contentLoaderInfo;
			}
			else
			{
				_urlLoader = new URLLoader();
				_urlLoader.dataFormat = _dataFormat;
			}
		}

		public function close():void
		{
			//一定要先清除回调函数
			clearFunc();
			try
			{
				if (_loader)
				{
					_loader.close();
				}
				if (_urlLoader)
				{
					_urlLoader.close();
				}
			}
			catch (e:Error)
			{

			}			
		}

		private function clearFunc():void
		{
			this.params = null;
			this.onComplete = null;
			this.onProgress = null;
			this.onError = null;
		}

		public function unload(url:String, stop:Boolean=true):void
		{
			close();

			if (_loader)
			{
				_loader.unload();
				_loader.unloadAndStop();
			}

			_loader = null;
			_loaderInfo = null;
			_urlLoader = null;
		}

		/**
		 * 加载单个资源，如果加载出错不会触发SLoaderEvent.complete事件
		 * @param params onComplete、onError和onProgress的回调参数
		 * @param onComplete 资源加载完成后调用，仅一次有效
		 * @param onError 资源加载发生错误后调用，仅一次有效
		 * @param onProgress 资源加载过程中调用，仅一次有效
		 */
		public function load(params:Array = null, 
			onComplete:Function = null, 
			onError:Function = null, 
			onProgress:Function = null):void
		{
			if (!url)
			{
				createUnCaughtError("无法加载空url");
				return;
			}

			this.params = params;
			this.onComplete = onComplete;
			this.onError = onError;
			this.onProgress = onProgress;

			try
			{
				if (_loader)
				{					
					var loaderContext:LoaderContext;
					if (useCurrentDomain)
					{
						loaderContext = new LoaderContext(checkPolicyFile, ApplicationDomain.currentDomain, 
							Security.sandboxType == Security.REMOTE ? SecurityDomain.currentDomain : null);
					}
					else if (checkPolicyFile)
					{
						loaderContext = new LoaderContext(checkPolicyFile);
					}

					addEvents(_loaderInfo);
					_loader.load(new URLRequest(fixUrl(url)), loaderContext);
				}
				else
				{
					addEvents(_urlLoader);
					_urlLoader.load(new URLRequest(fixUrl(url)));
				}
			}
			catch (e:Error)
			{
				createUnCaughtError(e.message);
			}
		}

		private function createUnCaughtError(err:String):void
		{
			onUnCaughtError(new UncaughtErrorEvent(UncaughtErrorEvent.UNCAUGHT_ERROR, false, true, err));
		}

		private function fixUrl(url:String):String
		{
			if (url.charAt(0) == "/")
			{
				url = url.substr(1);
			}

			if (revisionList && revisionList.hasOwnProperty(url))
			{
				url += (url.lastIndexOf("?") == -1 ? "?" : "&") + "rev=" + revisionList[url];
			}
			if (baseUrl)
			{
				url = baseUrl + url;
			}

			return url;
		}

		private function getFileType(url:String):String
		{
			var m:int = url.lastIndexOf("?");
			var n:int = (m != -1) ? url.substr(0, m).lastIndexOf(".") : url.lastIndexOf(".");
			var ext:String = n != -1 ? 
				url.substring(n + 1, m != -1 ? m : 2147483647).toLowerCase() : null;

			return ext && fileTypes[ext] ? fileTypes[ext] : SLoaderDataFormat.BINARY;
		}

		private function get fileTypes():Dictionary
		{
			return SLoader.fileTypes;
		}

		protected function addEvents(ld:IEventDispatcher):void
		{
			ld.addEventListener(Event.COMPLETE, onLoadCpt);
			ld.addEventListener(IOErrorEvent.IO_ERROR, onIoErr);
			ld.addEventListener(ProgressEvent.PROGRESS, onLoadProgress);
			ld.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError);
			if (ld is LoaderInfo)
			{
				LoaderInfo(ld).uncaughtErrorEvents.addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, onUnCaughtError);
			}
		}

		protected function removeEvents(ld:IEventDispatcher):void
		{
			ld.removeEventListener(Event.COMPLETE, onLoadCpt);
			ld.removeEventListener(IOErrorEvent.IO_ERROR, onIoErr);
			ld.removeEventListener(ProgressEvent.PROGRESS, onLoadProgress);
			ld.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError);
			if (ld is LoaderInfo)
			{
				LoaderInfo(ld).uncaughtErrorEvents.removeEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, onUnCaughtError);
			}
		}

		protected function onLoadCpt(e:Event):void
		{
			Logger.Log(this, "成功加载:" + url);

			dispatchEvent(new SLoaderEvent(SLoaderEvent.COMPLETE));

			if (onComplete != null)
			{
				onComplete.apply(null, params);
			}

			removeEvents(e.target as IEventDispatcher);
			clearFunc();
		}

		protected function onLoadProgress(e:Event):void
		{
			if (hasEventListener(SLoaderEvent.PROGRESS))
			{
				dispatchEvent(new SLoaderEvent(SLoaderEvent.PROGRESS));
			}

			if (onProgress != null)
			{
				onProgress.apply(null, params);
			}
		}

		protected function onIoErr(e:IOErrorEvent):void
		{
			_errorText = e.text;
			Logger.Warning(this, "IO错误，无法加载:" + url);
			errorHandler(e);
		}

		protected function onSecurityError(e:SecurityErrorEvent):void
		{
			_errorText = e.text;
			Logger.Warning(this, "安全限制，无法加载:" + url);
			errorHandler(e);
		}

		protected function onUnCaughtError(e:UncaughtErrorEvent):void
		{
			_errorText = e.text;
			Logger.Warning(this, "未知错误，无法加载:" + url);
			errorHandler(e);
		}

		private function errorHandler(e:Event):void
		{
			dispatchEvent(new SLoaderEvent(SLoaderEvent.ERROR));

			if (onError != null)
			{
				onError.apply(null, params);
			}

			removeEvents(e.target as IEventDispatcher);
			clearFunc();
		}

		/**
		 * 当前加载资源自身加载的百分比数，如58.9
		 * @return
		 */
		public function get curLoadPercent():Number
		{
			if (loaderInfo)
			{
				return Math.floor(1000 * loaderInfo.bytesLoaded / loaderInfo.bytesTotal) * 0.1;
			}

			if (urlLoader)
			{
				return Math.floor(1000 * urlLoader.bytesLoaded / urlLoader.bytesTotal) * 0.1;
			}

			return 0.0;
		}

		public function get content():*
		{
			if (loader)
			{
				return loader.content;
			}

			if (urlLoader)
			{
				return urlLoader.data;
			}

			return null;
		}

		/**
		 * 如果当前加载的是swf、图片等可视对象，则此值可用
		 * @return
		 */
		public function get contentDisplay():DisplayObject
		{
			return loader ? loader.content : null;
		}

		/**
		 * 如果当前加载的是swf，则此值可用
		 * @return
		 */
		public function get contentMovieClip():MovieClip
		{
			return loader ? loader.content as MovieClip : null;
		}

		/**
		 * 如果当前加载的是图片，则此值可用
		 * @return
		 */
		public function get contentBitmap():Bitmap
		{
			return loader ? loader.content as Bitmap : null;
		}

		/**
		 * 如果当前加载的是文本，则此值可用
		 * @return
		 */
		public function get contentText():String
		{
			return urlLoader ? urlLoader.data as String : null;
		}

		/**
		 * 如果当前加载的是二进制文件，则此值可用
		 * @return
		 */
		public function get contentBinary():ByteArray
		{
			return urlLoader ? urlLoader.data as ByteArray : null;
		}

		/**
		 * 如果当前加载资源的dataFormat==SLoaderDataFormat.DISPLAY，则此值可用
		 * @default
		 */
		public function get loader():Loader
		{
			return _loader;
		}

		/**
		 * 如果当前所加载资源的dataFormat==SLoaderDataFormat.DISPLAY，则此值可用
		 * @return
		 */
		public function get loaderInfo():LoaderInfo
		{
			return _loaderInfo;
		}

		/**
		 * 如果当前所加载资源的dataFormat是SLoaderDataFormat.TEXT、SLoaderDataFormat.BINARY,
		 * 则此值可用
		 * @return
		 */
		public function get urlLoader():URLLoader
		{
			return _urlLoader;
		}

		/**
		 * 当前加载的资源地址，如果所有资源都加载完成后，则是最后一个
		 * @default
		 */
		public function get url():String
		{
			return _url;
		}
		
		public function get bytesLoaded():uint
		{
			if(loaderInfo)
			{
				return loaderInfo.bytesLoaded;
			}
			
			if(urlLoader)
			{
				return urlLoader.bytesLoaded;
			}
			
			return 0;
		}
		
		public function get bytesTotal():uint
		{
			if(loaderInfo)
			{
				return loaderInfo.bytesTotal;
			}
			
			if(urlLoader)
			{
				return urlLoader.bytesTotal;
			}
			
			return 0;
		}
		
		public function get errorText():String
		{
			return _errorText;
		}

		public function get useCurrentDomain():Boolean
		{
			return _useCurrentDomain;
		}

		public function set useCurrentDomain(value:Boolean):void
		{
			_useCurrentDomain = value;
		}

		public function get checkPolicyFile():Boolean
		{
			return _checkPolicyFile;
		}

		public function set checkPolicyFile(value:Boolean):void
		{
			_checkPolicyFile = value;
		}

	}
}